﻿using System;
using System.Linq.Expressions;

namespace ExpressionToCodeLib {
	static class ExpressionPrecedence {
		static bool UnaryDashSym(ExpressionType et) {
			return et == ExpressionType.Negate
				|| et == ExpressionType.NegateChecked
#if DOTNET40
				|| et == ExpressionType.PreDecrementAssign
#endif
				;
		}
		static bool UnaryPlusSym(ExpressionType et) {
			return et == ExpressionType.UnaryPlus
#if DOTNET40
				|| et == ExpressionType.PreIncrementAssign
#endif
				;
		}
		public static bool TokenizerConfusable(ExpressionType a, ExpressionType b) {
			return UnaryDashSym(a) && UnaryDashSym(b) || UnaryPlusSym(a) && UnaryPlusSym(b);
		}

		public static int Rank(ExpressionType exprType) {
			switch (exprType) {
#if DOTNET40
				//brackets make no sense:
				case ExpressionType.Block: return -1;
				case ExpressionType.Goto: return -1;
				case ExpressionType.Loop: return -1;
				case ExpressionType.Switch: return -1;
				case ExpressionType.Throw: return -1;
				case ExpressionType.Try: return -1;
				case ExpressionType.Label: return -1;
#endif

				//brackets built-in; thus unnecesary (for params only!).
				case ExpressionType.MemberInit: return 1;
				case ExpressionType.ArrayIndex: return 1;
				case ExpressionType.Call: return 1;
				case ExpressionType.Invoke: return 1;
				case ExpressionType.New: return 1;
				case ExpressionType.NewArrayInit: return 1;
				case ExpressionType.NewArrayBounds: return 1;
				case ExpressionType.ListInit: return 1;
				case ExpressionType.Power: return 1;//non-native, uses Call.

				//other primary expressions
				case ExpressionType.Constant: return 1;
				case ExpressionType.Parameter: return 1;
				case ExpressionType.MemberAccess: return 1;
				case ExpressionType.ArrayLength: return 1;
#if DOTNET40
				case ExpressionType.Index: return 1;
				case ExpressionType.Default: return 1;
				case ExpressionType.PostIncrementAssign: return 1;
				case ExpressionType.PostDecrementAssign: return 1;
#endif

				//unary prefixes
				case ExpressionType.UnaryPlus: return 2;
				case ExpressionType.Negate: return 2;
				case ExpressionType.NegateChecked: return 2;
				case ExpressionType.Convert: return 2;
				case ExpressionType.ConvertChecked: return 2;
				case ExpressionType.Not: return 2;//bitwise OR numeric!
#if DOTNET40
				case ExpressionType.OnesComplement: return 2; //numeric
				case ExpressionType.IsTrue: return 2;//maybe?
				case ExpressionType.IsFalse: return 2;//maybe?
				case ExpressionType.PreIncrementAssign: return 2;
				case ExpressionType.PreDecrementAssign: return 2;
#endif

				//binary multiplicative
				case ExpressionType.Modulo: return 3;
				case ExpressionType.Multiply: return 3;
				case ExpressionType.MultiplyChecked: return 3;
				case ExpressionType.Divide: return 3;

				//binary addition
				case ExpressionType.Add: return 4;
				case ExpressionType.AddChecked: return 4;
				case ExpressionType.Subtract: return 4;
				case ExpressionType.SubtractChecked: return 4;
#if DOTNET40
				case ExpressionType.Decrement: return 4;//nonnative; uses ... - 1
				case ExpressionType.Increment: return 4;//nonnative; uses ... - 1
#endif

				//binary shift
				case ExpressionType.LeftShift: return 5;
				case ExpressionType.RightShift: return 5;

				//relational excl. equals
				case ExpressionType.LessThan: return 6;
				case ExpressionType.LessThanOrEqual: return 6;
				case ExpressionType.GreaterThan: return 6;
				case ExpressionType.GreaterThanOrEqual: return 6;
				case ExpressionType.TypeAs: return 6;
				case ExpressionType.TypeIs: return 6;

				//equality
				case ExpressionType.NotEqual: return 7;
				case ExpressionType.Equal: return 7;

				//bitwise/eager
				case ExpressionType.And: return 8;
				case ExpressionType.ExclusiveOr: return 9;
				case ExpressionType.Or: return 10;

				//logical/shortcircuit:
				case ExpressionType.AndAlso: return 11;
				case ExpressionType.OrElse: return 12;

				//null-coalesce
				case ExpressionType.Coalesce: return 13;

				//ternary ? : 
				case ExpressionType.Conditional: return 14;

				//assignments & lamba's
				case ExpressionType.Lambda: return 15;
				case ExpressionType.Quote: return 15;//maybe?
#if DOTNET40
				case ExpressionType.Assign: return 15;
				case ExpressionType.AddAssign: return 15;
				case ExpressionType.AndAssign: return 15;
				case ExpressionType.DivideAssign: return 15;
				case ExpressionType.ExclusiveOrAssign: return 15;
				case ExpressionType.LeftShiftAssign: return 15;
				case ExpressionType.ModuloAssign: return 15;
				case ExpressionType.MultiplyAssign: return 15;
				case ExpressionType.OrAssign: return 15;
				case ExpressionType.PowerAssign: return 15;
				case ExpressionType.RightShiftAssign: return 15;
				case ExpressionType.SubtractAssign: return 15;
				case ExpressionType.AddAssignChecked: return 15;
				case ExpressionType.MultiplyAssignChecked: return 15;
				case ExpressionType.SubtractAssignChecked: return 15;
#endif

				//Can't deal with these:
				/*
			case ExpressionType.Dynamic: return 0;//hmm...
			case ExpressionType.Extension: return 0;
			case ExpressionType.DebugInfo: return 0;//hmm...
			case ExpressionType.RuntimeVariables: return 0;//hmm...
			case ExpressionType.TypeEqual: return 0;//hmm...
			case ExpressionType.Unbox: return 0;//hmm...
				*/

				default: throw new ArgumentOutOfRangeException("Unsupported enum value:" + exprType);
			}
		}
	}
}
